package cz.cuni.mff.ufal.volk.langr;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.Map.Entry;

/**
 * Some auxiliary methods available in the package {@link cz.cuni.mff.ufal.volk.langr} only.
 * 
 * @author Bartłomiej Etenkowski
 */
class Utils {
  /** Just to make the class non-instantiable. */
  private Utils() {}
  
  /**
   * Increments the {@link Long} value associated with the given key. If no value is associated with the key
   * <tt>1L</tt> is stored.
   * 
   * @param <K> the type of keys maintained by the map
   * @param map the map
   * @param key the key
   */
  public static <K> void incrementLongValue(Map<K, Long> map, K key) {
    Long i = map.get(key);
    if (i == null)
      map.put(key, 1L);
    else
      map.put(key, i + 1L);
  }
  
  /**
   * Increments the {@link Integer} value associated with the given key. If no value is associated with the key
   * <tt>1</tt> is stored.
   * 
   * @param <K> the type of keys maintained by the map
   * @param map the map
   * @param key the key
   */
  public static <K> void incrementIntValue(Map<K, Integer> map, K key) {
    Integer i = map.get(key);
    if (i == null)
      map.put(key, 1);
    else
      map.put(key, i + 1);
  }
  
  /**
   * Selects the key in the map to which is associated the highest value.
   * 
   * @param <K> the type of keys maintained by the map
   * @param map the map
   * @return <tt>null</tt> if the map contains no values or all values are {@code null}, otherwise the key
   *         with the highest value associated
   */
  public static <K> K selectMostFrequent(Map<K, Integer> map) {
    Entry<K, Integer> best = null;
    for (Entry<K, Integer> e : map.entrySet()) {
      if (e.getValue() != null) {
        int val = e.getValue();
        if (best == null || best.getValue() < val)
          best = e;
      }
    }
    return best == null ? null : best.getKey();
  }
  
  /**
   * Retrieves the list of the sorted elements of the map. The elements are sorted by {@code Pair#value}. In case that
   * two or more equal values are stored under more keys the order of these elements is undefined.
   * @param <K> the type of keys maintained by the map 
   * @param <V> the type of values maintained by the map
   * @param map the map
   * @return the list of the sorted elements of the map
   */
  public static <K, V extends Comparable<V>> List<Pair<K, V>> getSortedItems(Map<K, V> map) {
    List<Pair<K, V>> sortedItems = new Vector<Pair<K, V>>(map.size());
    for (Entry<K, V> e : map.entrySet())
      sortedItems.add(new Pair<K, V>(e.getKey(), e.getValue()));
    Collections.sort(sortedItems);
    return sortedItems;
  }

  
  /**
   * A comparable pair of objects (auxiliary class). This class is necessary because the behavior of the {@link Entry}
   * objects is undefined outside the iteration.
   */
  public static class Pair<K, V extends Comparable<V>> implements Comparable<Pair<K, V>> {
    public Pair(K key, V value) {
      this.key = key;
      this.value = value;
    }
    final K key;
    final V value;
    
    @Override
    public int compareTo(Pair<K, V> o) {
      return value.compareTo(o.value);
    }
    
    @Override
    public String toString() {
      return String.format("%s (%s)", key.toString(), value.toString());
    }
  }
}
